﻿using System.Data;

using Tszy.Unlimited.Data.AdoNet.Sql.Common;
using Tszy.Unlimited.Data.AdoNet.Sql.Components;
using Tszy.Unlimited.Data.AdoNet.Sql.Components.Db.Mysql;
using Tszy.Unlimited.Data.AdoNet.Sql.Providers;

namespace Tszy.Unlimited.Data.AdoNet.Sql
{
    /// <summary>
    /// 数据访问器上下文。
    /// </summary>
    public interface ITszyDbContext
    {
        /// <summary>
        /// 数据提供者。
        /// </summary>
        IDataProvider DataProvider { get; }

        /// <summary>
        /// 增加。
        /// </summary>
        /// <typeparam name="TSource">实体类型。</typeparam>
        /// <param name="source">实体对象。</param>
        /// <param name="transaction"></param>
        /// <returns>是否成功。</returns>
        bool Add<TSource>(TSource source, IDbTransaction transaction = null);

        /// <summary>
        /// 增加。
        /// </summary>
        /// <typeparam name="TSource">实体类型。</typeparam>
        /// <param name="source">实体对象。</param>
        /// <param name="transaction"></param>
        /// <returns>是否成功。</returns>
        Task<bool> AddAsync<TSource>(TSource source, IDbTransaction transaction = null);

        /// <summary>
        /// 更新。
        /// </summary>
        /// <typeparam name="TSource"></typeparam>
        /// <returns></returns>
        IEditor<TSource> Edit<TSource>() where TSource : new();

        /// <summary>
        /// 删除。
        /// </summary>
        /// <typeparam name="TSource"></typeparam>
        /// <returns></returns>
        ICleaner<TSource> Remove<TSource>() where TSource : new();

        /// <summary>
        /// 查询。
        /// </summary>
        /// <typeparam name="TSource"></typeparam>
        /// <returns></returns>
        IQuery<TSource> Query<TSource>() where TSource : new();
    }

    /// <summary>
    /// MySQL 数据访问器上下文实例。
    /// </summary>
    public class TszyMysqlDbContext : ITszyDbContext
    {
        private readonly IDataProvider _dataProvider;

        private readonly IExecuteQueryFactory _executeQueryFactory;
        private readonly IExecuteEditorFactory _executeEditorFactory;
        private readonly IExecuteCleanerFactory _executeCleanerFactory;

        /// <summary>
        /// 初始化MySQL ORM 上下文实例。
        /// </summary>
        /// <param name="action">MySQL 数据库连接。</param>
        /// <param name="showSql">是否显示 SQL。</param>
        /// <param name="printSql">输出 SQL。</param>
        public TszyMysqlDbContext(Func<IDbConnection> action, bool showSql = false, Action<string> printSql = null)
        {
            _dataProvider = new DefaultDataProvider(action, DatabaseType.MySql);

            _executeQueryFactory = new DefaultExecuteQueryFactory(_dataProvider, showSql, printSql);
            _executeEditorFactory = new DefaultExecuteEditorFactory(_dataProvider, showSql, printSql);
            _executeCleanerFactory = new DefaultExecuteCleanerFactory(_dataProvider, showSql, printSql);
        }

        /// <summary>
        /// 数据提供者。
        /// </summary>
        public IDataProvider DataProvider => _dataProvider;

        /// <summary>
        /// 查询。
        /// </summary>
        /// <typeparam name="TSource"></typeparam>
        /// <returns></returns>
        public IQuery<TSource> Query<TSource>() where TSource : new()
        {
            return new Query<TSource>(_executeQueryFactory);
        }

        /// <summary>
        /// 更新。
        /// </summary>
        /// <typeparam name="TSource"></typeparam>
        /// <returns></returns>
        public IEditor<TSource> Edit<TSource>() where TSource : new()
        {
            return new Editor<TSource>(_executeEditorFactory);
        }

        /// <summary>
        /// 删除。
        /// </summary>
        /// <typeparam name="TSource"></typeparam>
        /// <returns></returns>
        public ICleaner<TSource> Remove<TSource>() where TSource : new()
        {
            return new Cleaner<TSource>(_executeCleanerFactory);
        }

        /// <summary>
        /// 增加。
        /// </summary>
        /// <typeparam name="TSource">实体类型。</typeparam>
        /// <param name="source">实体对象。</param>
        /// <param name="transaction"></param>
        /// <returns>是否成功。</returns>
        public bool Add<TSource>(TSource source, IDbTransaction transaction = null)
        {
            var tableName = DataModelManager.Instance.GetTableName<TSource>();

            if (typeof(DataModelIntBase).IsAssignableFrom(typeof(TSource)))
            {
                var identity = _dataProvider.AddAndGetKeyId(source, tableName: tableName, transaction: transaction);

                if (source is DataModelIntBase model)
                {
                    return model.ChangeCurrentIdentity(identity);
                }

                return true;
            }
            else if (typeof(DataModelLongBase).IsAssignableFrom(typeof(TSource)))
            {
                var identity = _dataProvider.AddAndGetKeyId(source, tableName: tableName, transaction: transaction);

                if (source is DataModelLongBase model)
                {
                    return model.ChangeCurrentIdentity(identity);
                }

                return true;
            }
            else
            {
                return _dataProvider.Add(source, tableName: tableName, transaction: transaction);
            }
        }

        /// <summary>
        /// 增加。
        /// </summary>
        /// <typeparam name="TSource">实体类型。</typeparam>
        /// <param name="source">实体对象。</param>
        /// <param name="transaction"></param>
        /// <returns>是否成功。</returns>
        public async Task<bool> AddAsync<TSource>(TSource source, IDbTransaction transaction = null)
        {
            var tableName = DataModelManager.Instance.GetTableName<TSource>();

            if (typeof(DataModelBase).IsAssignableFrom(typeof(TSource)))
            {
                var identity = await _dataProvider.AddAndGetKeyIdAsync(source, tableName: tableName, transaction: transaction);

                if (source is DataModelBase model)
                {
                    return model.ChangeCurrentIdentity(identity);
                }

                return true;
            }
            else
            {
                return _dataProvider.Add(source, tableName: tableName, transaction: transaction);
            }
        }
    }
}